namespace mecs {
    export abstract class BaseWorld implements IDataNode {

        /** 当前的world关注的系统 */
        systems: HashList<BaseSys>;
        /** 当前的world关注的实体 分类筛选实体，增加效率 */
        entitieHash: { [entityName: string]: HashList<BaseEntity> };
        /** 当前的world关注的实体*/
        entities: { [sid: number]: BaseEntity };
        /** 当前的world是否运行中 */
        starting = false;
        /** 当前世界内部逻辑帧 */
        logicFrame: LogicFrame;
        /**启动的 */
        protected mStartTime: number;
        /** 逻辑帧当前运行到的时间戳 */
        logicTime: number;
        /** 当前逻辑帧和上个逻辑帧直接间隔的时间，单位：ms */
        deltaLogicTime: number;
        /** 当前world内的随机种子 */
        seed: RandomSeed;
        /** world中的id生成器 */
        idCreater: WorldIdCreater;
        /**Comp创建器 */
        compCreater: CompCreater;

        /** 最大缓存帧数 */
        maxCacheFrameNum: number;
        /** 内存缓存的关键帧中，各个实体的各自组件的数据 */
        protected mKeyFrameCache: HashList<IKeyFrameCache>;

        constructor(seedNum: number, logicDelay: number = 20, maxCacheFrame = 0) {
            this.maxCacheFrameNum = maxCacheFrame;
            this.systems = new HashList([], 'sysName')
            this.entitieHash = {}
            this.entities = {}
            this.mKeyFrameCache = new HashList([], 'frameId');
            this.seed = new RandomSeed(seedNum);
            this.idCreater = new WorldIdCreater();
            this.compCreater = new CompCreater();

            this.deltaLogicTime = 0;
            this.mStartTime = Date.now();
            this.logicTime = 0;// 默认初始化的时候，将当前的时间赋值
            this.logicFrame = new LogicFrame(logicDelay)

            this.init()
        }

        /** 一般用来填充systems的初始化 */
        init(): void {
            this.mInitSys();
            this.mRegistComps();
            this.onInit();
        }

        /** 一般是timer的启动,为了通用，这里不直接写Laya.timer.xxx */
        start(): void {
            this.deltaLogicTime = this.logicFrame.delay;
            this.mStartTime = Date.now();
            this.logicTime = 0;
            this.starting = true;
            this.onStart()
        }

        resume() {
            this.starting = true;
            this.onStart();
        }

        pause() {
            this.stop();
        }

        /** 一般是timer结束 */
        stop(): void {
            this.starting = false;
            this.onStop()
        }

        protected abstract mInitSys();
        protected mRegistComps() { }
        protected onInit() { }
        protected abstract onStart();
        protected abstract onStop();

        /** 逻辑帧的循环，内部调用了update */
        protected mLogicLoop(): void {
            if (this.logicFrame.rollbackFrameId) {
                this.mRollbackFrame(this.logicFrame.rollbackFrameId)
                this.logicFrame.rollbackFrameId = 0;
            }
            let num = this.logicFrame.maxLogicFrameId - this.logicFrame.crtId;
            if (num > 1) {
                this.logicFrame.rollbacking = true;
                // 需要追帧
                while (num > 0) {
                    this.mUpdate()
                    num--;
                }
                this.logicFrame.rollbacking = false;
            } else {
                this.mUpdate()
            }
            this.mLogicLoopOver();
        }

        /** 每次逻辑帧循环结束后需要掉用的回调 */
        protected abstract mLogicLoopOver();

        /** 每次逻辑帧运行system中的update */
        protected mUpdate(): void {
            if (this.mChkNeedCacheFrame()) {
                this.mCacheFrame()
            }

            this.logicTime += this.deltaLogicTime;
            this.logicFrame.crtId++;
            this.systems.arr.forEach(sys => {
                sys.update(this.logicTime);
            });
        }

        /** 判断是否要缓存关键帧数据 */
        protected mChkNeedCacheFrame() {
            if (this.maxCacheFrameNum > 0) {
                let fd = this.logicFrame.getFrame(this.logicFrame.crtId + 1);
                if (!fd) return true; // 如果下一帧没有数据，则需要缓存
            }
            return false;
        }

        /** 缓存关键帧数据,使用者可以根据自身需求,覆盖此方法来自定义缓存实体,默认使用全实体缓存 */
        protected mCacheFrame(): void {
            let cache: IKeyFrameCache = this.cloneData();
            this.mKeyFrameCache.addData(this.logicFrame.crtId, cache)
            let n = this.mKeyFrameCache.length - this.maxCacheFrameNum;
            while (n > 0) {
                this.mKeyFrameCache.shiftData()
                n--;
            }
        }

        protected mCacheFilter(e: BaseEntity) {
            return true;
        }

        /** 回滚关键帧数据 */
        protected mRollbackFrame(frameId: number): void {
            let cache = this.mKeyFrameCache.getData(frameId);
            if (!cache) {
                console.log('没有这个关键帧数据:', frameId)
                return;
            }
            this.resetData(cache);
        }

        /** 在world中增加实体 */
        addEntity(entity: BaseEntity) {
            if (!this.entitieHash[entity.entityName]) {
                this.entitieHash[entity.entityName] = new HashList([], 'sid');
            }
            let hash = this.entitieHash[entity.entityName];
            hash.addData(entity.sid, entity);
            this.entities[entity.sid] = entity;
            this.addEntityToSystem(entity);
        }

        addEntityToSystem(entity: BaseEntity) {
            this.systems.arr.forEach(sys => {
                sys.addEntity(entity);
            })
        }

        /** 删除世界中所有的实体 */
        removeAllEntity(disposeEntity = false) {
            for (let i in this.entities) {
                this.removeEntity(this.entities[i], disposeEntity)
            }
        }

        /** 从world中删除实体,尽量传对象，别传sid */
        removeEntity(entity: BaseEntity | number, disposeEntity = false) {
            this.removeEntityFromSystem(entity);
            let tempEntity: mecs.BaseEntity;
            if (typeof entity == 'number') {
                tempEntity = this.entities[entity];
            } else {
                tempEntity=entity;
            }
            if (tempEntity) {
                let hash = this.entitieHash[tempEntity.entityName];
                if (hash) hash.removeData(tempEntity.sid);
                delete this.entities[tempEntity.sid];
            }
            if (disposeEntity && tempEntity) {
                tempEntity.dispose()
            }
        }

        removeEntityFromSystem(entity: BaseEntity | number) {
            this.systems.arr.forEach(sys => {
                sys.removeEntity(entity);
            })
        }
        /** 获取世界中，某个sid的实体 */
        getEntity(sid: number) {
            return this.entities[sid];
        }
        /** 获取当前世界中，某中实体名的集合 */
        getEntitys(entityName: string) {
            return this.entitieHash[entityName];
        }
        /** 通过System的cls来获取当前world中的某个system */
        getSystem<T>(cls: { new(world: BaseWorld): T, NAME: string }): T {
            return <any>this.systems.getData(cls.NAME);
        }

        /** 往当前的world中增加system,如果是帧同步网络游戏，不建议中途加入system */
        addSystem(sys: BaseSys) {
            for (let sid in this.entities) {
                let e = this.entities[sid]
                sys.addEntity(e)
            }
            this.mAddSystem(sys);
        }

        protected mAddSystem(sys: BaseSys) {
            this.systems.addData(sys.sysName, sys);
        }

        /** 往当前的world中删除system */
        removeSystem<T>(cls: { new(world: BaseWorld): T, NAME: string }) {
            let sys = this.systems.removeData(cls.NAME);
            if (sys) {
                sys.dispose()
            }
        }

        /** 往当前的world中增加system列表,如果是帧同步网络游戏，不建议中途加入system */
        addSystems(sysList: BaseSys[]) {
            sysList.forEach(sys => {
                this.mAddSystem(sys);
            })
        }

        /** 往当前的world中增加system列表,如果是帧同步网络游戏，不建议中途加入system */
        addSystemsByCls(...args: { new(world: BaseWorld): BaseSys, NAME: string }[]) {
            args.forEach(cls => {
                let s = new cls(this)
                this.mAddSystem(s);
            })
        }

        /**注册comp对象类，提供数据回滚组件自动创建 */
        regisComp(compCls: { NAME: string, new(): BaseComp }) {
            this.compCreater.regClsHash(compCls.NAME, compCls)
        }

        /**注册comp对象类，提供数据回滚组件自动创建 */
        regisComps(...compClses: { NAME: string, new(): BaseComp }[]) {
            for (let i = 0, len = compClses.length; i < len; i++) {
                this.regisComp(compClses[i]);
            }
        }

        resetData(cache: IKeyFrameCache) {
            this.logicTime = cache.logicTime;
            this.seed.reset(cache.seed);
            this.idCreater.resetId(cache.worldId);
            this.logicFrame.crtId = cache.frameId;
            cache.entities.forEach(e => {
                let temp = this.entities[e.sid];
                if (!temp) {
                    console.log('当前的实体不存在,重新从缓存创建', e.sid);
                    temp = EntityUtil.createEntityByCache(this, e)
                    this.addEntity(temp);
                } else {
                    temp.resetData(e);
                }
            })
            // 在回滚的时候，要将所有和世界中帧相关的所有事务，一起回滚，不然就有同步bug
            this.systems.forEach(sys => {
                let sysCache = cache.systems[sys.sysName];
                sys.resetData(sysCache);
            })
        }

        cloneData() {
            let cache: IKeyFrameCache = {
                seed: this.seed.crtSeedNum, worldId: this.idCreater.getCrtId(), frameId: this.logicFrame.crtId,
                logicTime: this.logicTime, entities: [], systems: {}
            }
            for (let i in this.entities) {
                let e = this.entities[i];
                if (!this.mCacheFilter(e)) continue;
                cache.entities.push(e.cloneData());
            }
            this.systems.forEach((item) => {
                cache.systems[item.sysName] = item.cloneData();
            })
            return cache;
        }

        dispose() {
            // 销毁当前世界
            this.starting = false;
            this.stop();
            if(this.logicFrame){
                this.logicFrame.init();
                this.logicFrame.dispose();
            }
            this.logicFrame = null;
            this.systems.arr.forEach(sys => sys.dispose());
            this.systems.dispose();
            this.systems = null;

            for (let i in this.entities) {
                this.entities[i].dispose();
                delete this.entities[i];
            }
            for (let i in this.entitieHash) {
                this.entitieHash[i].dispose();
                delete this.entitieHash[i];
            }
            this.entities = null;
            this.entitieHash = null;
            this.seed =null;
            this.idCreater =null;
            if(this.compCreater){
                this.compCreater.dispose()
            }
            this.compCreater = null;
            if(this.mKeyFrameCache){
                this.mKeyFrameCache.dispose()
            }
            this.mKeyFrameCache = null;
        }
    }
}